(function(root, factory) {

    if (typeof define === 'function' && define.amd) {
        define(factory);
    } else if (typeof exports !== 'undefined') {
        module.exports = factory();
    } else {
        root.lazyload = factory();
    }

}(this, function() {
    'use strict'

    /**
     * 定位
     * @type {{inviewport, abovethetop, belowthefold, rightoffold, leftofbegin}}
     */
    var position = (function() {

        var $window = $(window);

        // 底部
        var belowthefold = function(element, settings) {
            var fold;

            if (settings.container === undefined || settings.container === window) {
                fold = (window.innerHeight ? window.innerHeight : $window.height()) + $window.scrollTop();
            } else {
                fold = $(settings.container).offset().top + $(settings.container).height();
            }

            return fold <= $(element).offset().top - settings.threshold;
        };

        // 右边
        var rightoffold = function(element, settings) {
            var fold;

            if (settings.container === undefined || settings.container === window) {
                fold = $window.width() + $window.scrollLeft();
            } else {
                fold = $(settings.container).offset().left + $(settings.container).width();
            }

            return fold <= $(element).offset().left - settings.threshold;
        };

        // 顶部
        var abovethetop = function(element, settings) {
            var fold;

            if (settings.container === undefined || settings.container === window) {
                fold = $window.scrollTop();
            } else {
                fold = $(settings.container).offset().top;
            }

            return fold >= $(element).offset().top + settings.threshold + $(element).height();
        };

        // 左边
        var leftofbegin = function(element, settings) {
            var fold;

            if (settings.container === undefined || settings.container === window) {
                fold = $window.scrollLeft();
            } else {
                fold = $(settings.container).offset().left;
            }

            return fold >= $(element).offset().left + settings.threshold + $(element).width();
        };

        // 正中间
        var inviewport = function(element, settings) {
            return !rightoffold(element, settings) && !leftofbegin(element, settings) &&
                !belowthefold(element, settings) && !abovethetop(element, settings);
        };
        return {
            inviewport: inviewport,
            abovethetop: abovethetop,
            belowthefold: belowthefold,
            rightoffold: rightoffold,
            leftofbegin: leftofbegin
        };
    }());

    /**
     * jquery 滚动加载
     * @param options
     * @returns {$}
     */
    $.fn.scrollload = function(options) {
        var $window = $(window);
        var elements = this;
        var $container;
        var settings = {
            threshold: 0,
            failure_limit: 0,
            event: "scroll",
            effect: "show",
            delay: 300,
            container: window,
            skip_invisible: true,
            appear: null, // @Function
            placeholder: ""
        };

        function update() {
            var counter = 0;

            elements.each(function() {
                var $this = $(this);
                if (settings.skip_invisible && !$this.is(":visible")) {
                    return;
                }
                if (position.abovethetop(this, settings) ||
                    position.leftofbegin(this, settings)) {
                    /* Nothing. */
                } else if (!position.belowthefold(this, settings) &&
                    !position.rightoffold(this, settings)) {

                    // 延时执行, 滚得太快就不加载了
                    if (!this.loaded) {
                        setTimeout(function() {
                            if (!$this[0].loaded) {
                                if (position.inviewport($this, settings)) {
                                    $this.trigger("appear");
                                }
                            }
                        }, settings.delay);
                    }
                    /* if we found an image we'll load, reset the counter */
                    counter = 0;
                } else {
                    if (++counter > settings.failure_limit) {
                        return false;
                    }
                }
            });

            var temp = $.grep(elements, function(element) {
                return !element.loaded;
            });
            elements = $(temp);
        }

        if (options) {
            /* Maintain BC for a couple of versions. */
            if (undefined !== options.failurelimit) {
                options.failure_limit = options.failurelimit;
                delete options.failurelimit;
            }
            if (undefined !== options.effectspeed) {
                options.effect_speed = options.effectspeed;
                delete options.effectspeed;
            }
            $.extend(settings, options);
        }

        /* Cache container as jQuery as object. */
        $container = (settings.container === undefined ||
            settings.container === window) ? $window : $(settings.container);

        /* Fire one scroll event per scroll. Not one scroll event per image. */
        if (0 === settings.event.indexOf("scroll")) {
            $container.bind(settings.event, function() {
                return update();
            });
        }

        this.each(function() {
            var self = this;
            var $self = $(self);

            self.loaded = false; // 加载渲染完成

            /* 图片给一张默认的图片,不会显得太空白 */
            if ($self.is("img") && ($self.attr("src") === undefined || $self.attr("src") === false)) {
                $self.attr("src", settings.placeholder);
            }

            /* 渲染显示到浏览器 */
            $self.one("appear", function() {
                if (!this.loaded) {
                    if ($.isFunction(settings.appear)) {
                        this.loaded = true;
                        settings.appear.call(this);
                    }
                } else {
                    $(this).unbind('appear', settings.appear);

                    /* Remove image from array so it is not looped next time. */
                    var temp = $.grep(elements, function(element) {
                        return !element.loaded;
                    });
                    elements = $(temp);
                }
            });

            /* When wanted event is triggered load original image */
            /* by triggering appear.                              */
            if (0 !== settings.event.indexOf("scroll")) {
                $self.bind(settings.event, function() {
                    if (!self.loaded) {
                        $self.trigger("appear");
                    }
                });
            }
        });

        /* Check if something appears when window is resized. */
        $window.bind("resize", function() {
            update();
        });

        /* Force initial check if images should appear. */
        $(document).ready(function() {
            update();
        });

        return this;
    };


    /**
     * 根据jquery滚动加载原理,进行懒加载
     */
    var lazyload = (function() {
        var lazy = {};

        lazy.event = function(selector, fn) {
            $(selector).scrollload({
                threshold: 200,
                appear: fn
            });
        };


        lazy.loadComponent = function(selector, fn) {
            $(selector).scrollload({
                threshold: 200,
                appear: function() {
                    var source = $(this).attr("data-source");
                    if ($.isFunction(fn))
                        fn.call(this, this);
                    else if (window.dataSource && $.isFunction(window.dataSource[source]))
                        window.dataSource[source].call(this, this);
                }
            })
        };


        lazy.image = function(selector) {
            $(selector).scrollload({
                threshold: 0,
                delay: 0,
                skip_invisible: false,
                appear: function() {
                    var self = this;
                    var src = $(this).attr("data-src");
                    $("<img />")
                        .bind("load", function() {
                            $(self).hide();
                            if ($(self).is("img")) {
                                $(self).attr("src", src);
                            } else {
                                $(self).css("background-image", "url('" + src + "')");
                            }
                            $(self).show();

                            $(self).loaded = true;
                        }).attr("src", src);
                }
            });
        };
        return lazy;
    }());

    return lazyload;
}));